#pragma once
#include "DataPoint.h"
#include "ImportData.h"
#include <iostream>
#include <Eigen/Eigen>

using namespace std;
using namespace Eigen;

class Convoluter
{
public:
	Convoluter(ImportData* d);
	void initialize2d();
	void initialize3d();
	void computeDerivatives(int slice, 
        MatrixXd& outDx,
        MatrixXd& outDy,
        MatrixXd& outDxDx,
        MatrixXd& outDxDy,
        MatrixXd& outDyDy) const;
	void computeDerivatives3d(std::vector<MatrixXd>& outDx,std::vector<MatrixXd>& outDy,std::vector<MatrixXd>& outDz,
		std::vector<MatrixXd>& outDxDx,std::vector<MatrixXd>& outDyDy,std::vector<MatrixXd>& outDzDz,
		std::vector<MatrixXd>& outDxDy,std::vector<MatrixXd>& outDxDz,std::vector<MatrixXd>& outDyDz) const;

	~Convoluter(void);

private:
	ImportData* data;
	MatrixXd dxFilter, dyFilter;
	std::vector<MatrixXd> dzFilter3d,dxFilter3d,dyFilter3d,dxdxFilter3d,dydyFilter3d,dzdzFilter3d,dxdzFilter3d,dxdyFilter3d,dydzFilter3d;
	MatrixXd dxdxFilter,dxdyFilter,dydyFilter;
	void convolute(DataPoint** in, const MatrixXd& filter,MatrixXd& out) const;
	void convolute3D(const std::vector<MatrixXd>& filter,std::vector<MatrixXd>& out) const;
	void convolute_second_3D(std::vector<MatrixXd>& in,const std::vector<MatrixXd>& filter,std::vector<MatrixXd>& out) const;
	double getValueAt(DataPoint** in, int x, int y) const;
	double getValueAt3d(int x, int y,int z) const;
	double getValueAt_second_3d(std::vector<MatrixXd>& in,int x, int y,int z) const;
};
